<?php

/**
 * Tpl类
 */
class Tpl
{
	
	// 模板文件路径
	protected $viewDir = './view/';
	// 生成缓存文件路径
	protected $cacheDir = './cache/';
	// 缓存文件有效期
	protected $lifeTime = 3600;
	// 变量集
	protected $args;

	function __construct($viewDir = null, $cacheDir = null, $lifeTime = null)
	{
		if(!empty($viewDir)){
			if($this->checkDir($viewDir)){
				$this->viewDir = $viewDir;
			}
		}
		if(!empty($cacheDir)){
			if($this->checkDir($cacheDir)){
				$this->cacheDir = $cacheDir;
			}
		}
		if(!empty($lifeTime)){
			$this->lifeTime = $this->lifeTime;
		}
	}

	// 判断该路径是否存在，是否可写
	protected function checkDir($dirPath)
	{
		// 如果当前文件不存在或者不是文件路径，则创建文件
		if(!file_exists($dirPath) || !is_dir($dirPath)){
			return mkdir($dirPath, 0755, true);
		}
		// 判断该文件是否有权限,可以读写
		if(!is_readable($dirPath) || !is_writable($dirPath)){
			return chmod($dirPath, 0755);
		}
		return true;
	}

	// 添加变量 $tpl->assign('title', $title);
	function assign($name, $value)
	{
		$this->args[$name] = $value;
	}

	// 展示缓存文件方法
	// $viewName:模板文件名 $isInclude:模板文件是否仅仅只需要编译；还是先编译在再包含进来
	// $uri: index.php?page=1,为了让缓存文件名不重复，将文件名和url拼接起来在md5，审查缓存文件名
	function display($viewName, $isInclude = true, $uri = null)
	{
		// 组装模板文件全路径
		$viewPath = rtrim($this->viewDir, '/').'/'.$viewName;
		// 组装缓存文件全路径
		$cachePath = rtrim($this->cacheDir, '/').'/'.md5($viewName.$uri).'.php';
		// 根据缓存文件全路径判断缓存文件是否存在，
		$iscachePath = file_exists($cachePath)? ture : false;
		// 如果缓存文件不存在，编译模板文件，生成缓存文件
		if(!$iscachePath){
			// 编译模板文件
			$php = $this->compiler($viewName);
			// 生成缓存文件
			file_put_contents($cachePath, $php);
		}else{
			
			// 如果缓存文件存在，第一，判断缓存文件是否过期；第二，判断模板文件是否修改，如果模板文件被修改则重新生成；
			$isTimeOut = (filectime($cachePath) + $this->lifeTime) > time() ? false : true;
			$isChange = filectime($viewPath) > filectime($cachePath) ? true : false;
			// 缓存文件重新生成
			if($isTimeOut || $isChange){
				// 编译模板文件
				$php = $this->compiler($viewName);
				// 生成缓存文件
				file_put_contents($cachePath, $php);
			}
		}
		
		// 判断缓存文件是否需要包含进来
		if($isInclude){
			// 将变量解析出来
			extract($this->args);
			// 展示缓存文件
			include $cachePath;
		}
	}

	// compiler方法，解析HTML文件
	protected function compiler($filePath)
	{
		// 读取文件内容
		$html = file_get_contents($filePath);
		// 正则替换
		$array = [
			'{$%%}' => '<?=$\1; ?>',
			'{foreach %%}' => '<?php foreach (\1): ?>',
			'{/foreach}' => '<?php endforeach; ?>',
			'{include %%}' => '',
			'{if %%}' => '<?php if(\1): ?>',
		];
		// 遍历数组，将%%全部修改为.+,然后执行正则替换
		foreach ($array as $key => $value) {
			// 生成正则表达式
			$pattern = '#'.str_replace('%%', '(.+?)', preg_quote($key, '#')).'#';
			// 实现正则替换
			if(strstr($pattern, 'include')) {
				$html = preg_replace_callback($pattern, [$this, 'parseInclude'], $html);
			}else{
				$html = preg_replace($pattern, $value, $html);
			}
		}
		// 返回被替换的文件内容
		return $html;
	}

	// 处理include正则表达式，$data是匹配到的内容
	protected function parseInclude($data)
	{
		// 将文件名两边的引号去除掉
		$fileName = trim($data[1], '\'"');
		// 然后不包含文件生成缓存
		$this->compiler($fileName, false);
		// 拼接缓存文件全路径
		$cacheName = md5($fileName).'.php';	
		$cachePath = rtrim($this->cacheDir, '/').'/'.$cacheName;
		return '<?php include "'.$cachePath.'"?>';
	}
}